import axios from 'axios'
import type { Method } from 'axios'
import { useUserStore } from '@/stores'
import { showToast } from 'vant'
import router from '@/router'

const baseURL = 'https://consult-api.itheima.net/'
// 创建实例
const instance = axios.create({
  baseURL,
  timeout: 10000
})

// 配置请求拦截器(注入 token)
instance.interceptors.request.use(
  // 成功
  (config) => {
    // TODO: 测试token注入
    // 如果仓库中发现了 token 放入到请求头当中, 放入的形式是固定的
    const store = useUserStore()
    if (store.user?.token && config.headers) {
      config.headers.Authorization = `Bearer ${store.user.token}`
    }

    return config
  },
  (err) => {
    // 失败
    return Promise.reject(err)
  }
)
// 配置响应拦截
instance.interceptors.response.use(
  (res) => {
    // 哪怕是响应状态码是 200 正常响应, 不代表数据就正确
    // 这个项目中我们跟后端约定了如果请求成功进行, 而且数据完全正确, res.data.code 就等于 10000
    if (res.data.code !== 10000) {
      // TODO: 测试请求成功, 数据失败
      // 报错
      showToast(res.data.message || '数据错误')
      // 拒绝当前请求
      return Promise.reject(res.data)
    }
    // 这里是完全成功的部分, 为了之后的获取数据方便, 先将一层 res.data 解开
    return res.data
  },
  (err) => {
    // 如果响应 4xx / 5xx
    // 网络请求本身出现了问题, 一般直接报错返回即可
    // 这里有一个特殊情况, token 失效, 需要清理用户数据, 还要跳转登录页
    // 跟后端的约定是 如果 token 有问题, 状态码为 401
    if (err.response?.status === 401) {
      // TODO: 测试token失效
      const store = useUserStore()
      store.delUser()
      router.push('/login')
    }
    return Promise.reject(err)
  }
)
// 封装同时适合于不同请求方法的通用函数
const request = <T>(
  url: string,
  method: Method = 'GET',
  submitData?: object
) => {
  type ResType = {
    code: number
    message: string
    data: T
  }
  return instance<T, ResType>({
    url,
    method,
    // params: submitData (get)
    // data: submitData (post, put, delete)
    // 中括号里面的内容, 会作为代码运行得出结果,在变成键名
    // 先将 method 转成大写, 看看是不是 GET, 然后决定, 传数据是 params 还是 data
    [method.toUpperCase() === 'GET' ? 'params' : 'data']: submitData
  })
}

export { instance, baseURL, request }

// // 以下是泛型讲解代码, 暂时全部注释
// type myType = {
//   code: number
//   data: {
//     account: string
//     avatar: string
//     id: string
//     mobile: string
//     refreshToken: string
//     token: string
//   }
//   message: string
// }

// const fn = async () => {
//   // axios 泛型第一个参数, 可以定义 res.data 的类型
//   // 如果觉得不满意,想要全部重写定义, 可以传入第二个参数
//   // 其中第二个参数的泛型, 应该根据后台响应数据进行编写
//   const res = await instance<myType, myType>({
//     url: '/abc'
//   })
//   res.
// }
